/*
 * Copyright (c) 2018-2999 湖南洛云网络科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.yami.shop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import com.yami.shop.bean.app.dto.MyOrderDto;
import com.yami.shop.bean.app.dto.OrderCountData;
import com.yami.shop.bean.app.dto.ShopCartOrderMergerDto;
import com.yami.shop.bean.enums.*;
import com.yami.shop.bean.event.*;
import com.yami.shop.bean.model.*;
import com.yami.shop.bean.param.OrderParam;
import com.yami.shop.common.config.Constant;
import com.yami.shop.bean.param.*;
import com.yami.shop.common.enums.PayType;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.util.*;
import com.yami.shop.dao.*;
import com.yami.shop.dao.OrderMapper;
import com.yami.shop.dao.OrderRefundMapper;
import com.yami.shop.dao.ProductMapper;
import com.yami.shop.dao.SkuMapper;
import com.yami.shop.service.FormService;
import com.yami.shop.service.OrderItemService;
import com.yami.shop.service.OrderService;
import com.yami.shop.service.OrderSettlementService;
import com.yami.shop.service.*;
import lombok.AllArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

/**
 * @author lgh on 2018/09/15.
 */
@Service
@AllArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    private final OrderMapper orderMapper;
    private final DeliveryService deliveryService;
    private final NotifyTemplateService notifyTemplateService;
    private final StationService stationService;

    private final SkuMapper skuMapper;
    private final PayManagerService payManagerService;

    private final OrderItemService orderItemService;

    private final OrderSettlementService orderSettlementService;

    private final ProductMapper productMapper;

    private final ApplicationEventPublisher eventPublisher;
    private final OrderLangMapper orderlangMapper;

    private final OrderRefundMapper orderRefundMapper;

    private final FormService formService;

    private final UserMapper userMapper;

    private final UserExtensionMapper userExtensionMapper;

    private final FlowPageAnalyseUserMapper flowPageAnalyseUserMapper;

    private final OrderRetailProdLogService orderRetailProdLogService;
    private final PayService payService;

    @Override
    public Order getOrderByOrderNumber(String orderNumber) {
        return orderMapper.getOrderByOrderNumber(orderNumber);
    }

    @Override
    public Order getOrderByOrderNumberAndUserId(String orderNumber, String userId, boolean valid) {
        Order order = orderMapper.getOrderByOrderNumberAndUserId(orderNumber, userId);
        if (valid && Objects.isNull(order)) {
            // 订单不存在
            throw new YamiShopBindException("yami.order.no.exist");
        }
        return order;
    }

    @Override
    public Order getOrderByOrderNumberAndShopId(String orderNumber, Long shopId, boolean valid) {
        Order order = orderMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, orderNumber).eq(Order::getShopId, shopId));
        if (valid && order == null) {
            // 订单不存在
            throw new YamiShopBindException("yami.order.no.exist");
        }
        return order;
    }


    @Override
    @CacheEvict(cacheNames = "ConfirmOrderCache", key = "#userId")
    public void removeConfirmOrderCache(String userId) {
    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Order> submit(String userId, ShopCartOrderMergerDto mergerOrder) {
        List<Order> orderList = new ArrayList<>();

        // 提交订单
        eventPublisher.publishEvent(new SubmitOrderEvent(mergerOrder, orderList));
        // 插入订单
        orderList.forEach(order -> orderMapper.insert(order));
        // 保存订单语言表
        orderlangMapper.insertBatchOrderLang(orderList);
        List<OrderItem> orderItems = orderList.stream().flatMap(order -> order.getOrderItems().stream()).collect(Collectors.toList());
        // 插入订单项，返回主键
        orderItemService.insertBatchOrderItem(orderItems);
        //计算积分情况
        if (mergerOrder.getIsScorePay() != null && mergerOrder.getIsScorePay() == 1) {
            eventPublisher.publishEvent(new SubmitScoreOrderEvent(mergerOrder, orderList));
        }
        return orderList;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delivery(Order order) {
        orderMapper.updateById(order);
        // 消息推送-发货提醒
        Delivery delivery = deliveryService.getById(order.getDvyId());
        String dvyName = "";
        if(delivery.getDvyName() != null){
            dvyName = delivery.getDvyName();
        }
        notifyTemplateService.sendNotifyOfDelivery(order,dvyName, SendType.DELIVERY);
    }



    @Override
    public List<Order> listUnRefundOrderAndOrderItems(Integer orderStatus, DateTime lessThanUpdateTime) {
        return orderMapper.listUnRefundOrderAndOrderItems(orderStatus, lessThanUpdateTime);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrders(List<Order> orders) {
        if (CollectionUtil.isEmpty(orders)) {
            return;
        }
        orderMapper.cancelOrders(orders);
        List<OrderItem> allOrderItems = new ArrayList<>();
        List<OrderRetailProdLog> list = new ArrayList<>();
        List<Long> retailProdIds = new ArrayList<>();
        List<Long> retailSkuIds = new ArrayList<>();
        for (Order order : orders) {
            List<OrderItem> orderItems = order.getOrderItems();
            allOrderItems.addAll(orderItems);
            eventPublisher.publishEvent(new CancelOrderEvent(order));
            // 还原单品商品的库存,如果是组合商品
            List<OrderRetailProdLog> orderRetailProdLogs =  orderRetailProdLogService.returnStocksByOrderNumber(order.getOrderNumber());
            if (CollUtil.isNotEmpty(orderRetailProdLogs)) {
                list.addAll(orderRetailProdLogs);
            }
        }
        if (CollUtil.isNotEmpty(list)) {
            retailProdIds = list.stream().map(OrderRetailProdLog::getProdId).collect(Collectors.toList());
            retailSkuIds = list.stream().map(OrderRetailProdLog::getSkuId).collect(Collectors.toList());
        }
        // 查询所有的商品
        allOrderItems.stream().map(OrderItem::getProdId);
        // 如果是秒杀订单也不必还原库存
        if (CollectionUtil.isEmpty(allOrderItems) || OrderType.SECKILL.value().equals(orders.get(0).getOrderType())) {
            return;
        }
        // 过滤掉已经还原了单品的库存的商品
        Map<Long, Integer> prodCollect = new HashMap<>(16);
        List<Long> finalRetailProdIds = retailProdIds;
        allOrderItems.stream().collect(Collectors.groupingBy(OrderItem::getProdId)).forEach((prodId, orderItems) -> {
            int prodTotalNum = orderItems.stream().mapToInt(OrderItem::getProdCount).sum();
            if (!finalRetailProdIds.contains(prodId)) {
                prodCollect.put(prodId, prodTotalNum);
            }
        });
        if (!prodCollect.isEmpty()) {
            productMapper.returnStock(prodCollect);
        }
        List<Long> finalRetailSkuIds = retailSkuIds;
        Map<Long, Integer> skuCollect = new HashMap<>(16);
        allOrderItems.stream().collect(Collectors.groupingBy(OrderItem::getSkuId)).forEach((skuId, orderItems) -> {
            int prodTotalNum = orderItems.stream().mapToInt(OrderItem::getProdCount).sum();
            if (!finalRetailSkuIds.contains(skuId)) {
                skuCollect.put(skuId, prodTotalNum);
            }
        });
        if (!skuCollect.isEmpty()) {
            skuMapper.returnStock(skuCollect);
        }
        // 查询出微信支付并且为二维码支付的，失效掉对应支付二维码
        List<String> orderNumbers = new ArrayList<>();
        for (Order order : orders) {
            if(Objects.isNull(order.getPayType())){
                continue;
            }
            // 如果是支付宝支付，或者微信 二维码支付直接使二维码失效
            if (Objects.equals(order.getPayType(), PayType.ALIPAY.value()) ||
                    Objects.equals(order.getPayType(), PayType.WECHATPAY_SWEEP_CODE.value())){
                orderNumbers.add(order.getOrderNumber());
            }
        }
        if(CollectionUtils.isEmpty(orderNumbers)){
            return;
        }
        // 如果有重复的payNo，不用失效掉二维码。
        List<OrderSettlement> filterOrderSettlement = orderSettlementService.listByOrderNumbers(orderNumbers);
        try {
            payManagerService.cancelOrderQrCode(filterOrderSettlement);
        }catch (Exception e){
            e.printStackTrace();
            log.error("失效支付二维码失败");
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receiptOrder(List<Order> orders) {
        orderMapper.receiptOrder(orders);
        for (Order order : orders) {
            eventPublisher.publishEvent(new ReceiptOrderEvent(order));
            // 消息推送-用户已确认收货
            notifyTemplateService.sendNotifyByUserRefundDelivery(order, SendType.RECEIPT_ORDER);
        }

    }

//    @Override
//    public List<Order> listOrdersDetialByOrder(Order order, Date startTime, Date endTime) {
//        return orderMapper.listOrdersDetialByOrder(order, startTime, endTime);
//    }

    @Override
    public IPage<Order> pageOrdersDetailByOrderParam(Page<Order> page, OrderParam orderParam) {
        List<Order> orders = orderMapper.listOrdersDetailByOrderParam(new PageAdapter(page), orderParam);
        for (Order order : orders) {
            if(StrUtil.isNotBlank(order.getReceiverName()) && CharUtil.length(order.getReceiverName()) > 8){
                order.setReceiverName(order.getReceiverName().substring(0, Math.min(8, order.getReceiverName().length() - 1)) + "...");
            }
        }
        page.setRecords(orders);
        page.setTotal(orderMapper.countOrderDetail(orderParam));
        return page;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteOrders(List<Order> orders) {
        orderMapper.deleteOrders(orders);
    }

    @Override
    public OrderCountData getOrderCount(String userId) {
        // 获取普通订单数据
        OrderCountData countData = orderMapper.getOrderCount(userId);
        // 获取退款订单数据
        countData.setRefund(orderRefundMapper.getRefundCount(userId));
        return countData;
    }

    @Override
    public double sumTotalDistributionAmountByOrderItem(List<OrderItem> orderItems) {
        // 订单总分销金额
        double totalDistributionAmount = 0.0;
        if (CollectionUtil.isNotEmpty(orderItems)) {
            for (OrderItem orderItem : orderItems) {
                if (StrUtil.isBlank(orderItem.getDistributionCardNo())) {
                    continue;
                }
                // 如果改订单项已经退款了的话，分销员的佣金就已经回退了，不需要继续算钱
                if (Objects.equals(orderItem.getReturnMoneySts(), ReturnMoneyStsType.SUCCESS.value())) {
                    continue;
                }
                // 分销佣金
                if (orderItem.getDistributionAmount() != null && orderItem.getDistributionAmount() > 0) {
                    totalDistributionAmount = Arith.add(totalDistributionAmount, orderItem.getDistributionAmount());
                }
                // 上级分销佣金
                if (orderItem.getDistributionParentAmount() != null && orderItem.getDistributionParentAmount() > 0) {
                    totalDistributionAmount = Arith.add(totalDistributionAmount, orderItem.getDistributionParentAmount());
                }
            }
        }
        return totalDistributionAmount;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeAmount(Order order) {
        Order orderDb = orderMapper.selectOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getOrderId, order.getOrderId())
                .eq(Order::getOrderNumber, order.getOrderNumber())
        );
        if (!Objects.equals(orderDb.getStatus(), OrderStatus.UNPAY.value())){
            // 订单状态异常，无法更改订单金额
            throw new YamiShopBindException("yami.order.unrecognized.update");
        }
        Integer changeAmountVersion = orderDb.getChangeAmountVersion() + 1;
        //减少金额
        double amount = 0.00;
        //平台优惠金额
        double platformAmount = 0.00;
        Map<Long, OrderItem> collect = order.getOrderItems().stream().collect(Collectors.toMap(OrderItem::getOrderItemId, orderItem -> orderItem));
        List<OrderItem> orderItemList = orderItemService.list(new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderNumber, order.getOrderNumber()));
        for (OrderItem orderItemDb:orderItemList){
            OrderItem orderItem = collect.get(orderItemDb.getOrderItemId());
            if (orderItem.getChageAmount() >= orderItemDb.getActualTotal() || orderItem.getChageAmount() < 0){
                // 订单金额必须大于0，或者数据发生变化(请刷新后重试)
                throw new YamiShopBindException("yami.order.amount.check");
            }
            if (Objects.isNull(orderItem.getChageAmount()) || Objects.equals(orderItem.getChageAmount(),0.00)){
                continue;
            }
            //修改平台优惠金额
            double playformItemAmount = 0.00;
            if (orderItemDb.getPlatformShareReduce() > 0){
                //金额减少比例 = 减少金额  % 总金额
                double proportion = Arith.div(orderItem.getChageAmount(),orderItemDb.getProductTotalAmount(),6);
                //平台优惠金额减少金额 = 减少比例 * 原平台优惠金额
                playformItemAmount = Arith.round(Arith.mul(proportion,orderItemDb.getPlatformShareReduce()),2);
                //平台优惠金额 = 原平台优惠金额 - 对应比例的金额
                orderItemDb.setPlatformShareReduce(Arith.sub(orderItemDb.getPlatformShareReduce(),playformItemAmount));
            }
            //实际金额 = 原实际金额 - 变化金额
            orderItemDb.setActualTotal(Arith.sub(orderItemDb.getActualTotal(),orderItem.getChageAmount()));
            //总优惠金额 = 变化金额 + 原总优惠金额
            orderItemDb.setShareReduce(Arith.add(orderItem.getChageAmount(),orderItemDb.getShareReduce()));

            orderItemDb.setChangeAmountVersion(changeAmountVersion);

            amount = Arith.add(amount,orderItem.getChageAmount());
            platformAmount = Arith.add(platformAmount,playformItemAmount);
        }
        orderItemService.updateBatchById(orderItemList);
        OrderSettlement orderSettlement = orderSettlementService.getOne(new LambdaQueryWrapper<OrderSettlement>().eq(OrderSettlement::getOrderNumber, order.getOrderNumber()));
        //修改订单信息
        orderDb.setActualTotal(Arith.sub(orderDb.getActualTotal(),amount));
        orderDb.setReduceAmount(Arith.add(orderDb.getReduceAmount(),amount));
        orderDb.setPlatformAmount(Arith.sub(orderDb.getPlatformAmount(),platformAmount));
        //初始化
        amount = 0.00;
        // 计算运费变化金额
        if(!Objects.equals(orderDb.getFreightAmount(),order.getFreightAmount())){
            amount = Arith.add(amount,Arith.sub(order.getFreightAmount(),orderDb.getFreightAmount()));
        }
        orderDb.setActualTotal(Arith.add(orderDb.getActualTotal(),amount));
        orderSettlement.setPayAmount(orderDb.getActualTotal());
        orderSettlement.setChangeAmountVersion(changeAmountVersion);
        orderSettlementService.updateById(orderSettlement);
        //修改运费信息
        orderDb.setFreightAmount(order.getFreightAmount());
        orderDb.setChangeAmountVersion(changeAmountVersion);
        orderMapper.updateById(orderDb);
    }

    @Override
    public void soldExcel(OrderParam orderParam, HttpServletResponse response) {
        List<Order> orders = orderMapper.listOrdersDetailByOrderInfo(orderParam);

        //通过工具类创建writer
        ExcelWriter writer = ExcelUtil.getWriter();
        // 待发货
        String[] hearder = {"订单编号", "下单时间", "收件人", "手机", "收货地址", "商品名称", "数量", "订单应付", "订单运费", "订单实付", "订单状态", "售后状态"};
        Sheet sheet = writer.getSheet();
        sheet.setColumnWidth(0, 20 * 256);
        sheet.setColumnWidth(1, 20 * 256);
        sheet.setColumnWidth(3, 20 * 256);
        sheet.setColumnWidth(4, 60 * 256);
        sheet.setColumnWidth(5, 60 * 256);

        writer.merge(hearder.length - 1, "销售信息整理");
        writer.writeRow(Arrays.asList(hearder));

        int row = 1;
        for (Order dbOrder : orders) {
            UserAddrOrder addr = dbOrder.getUserAddrOrder();
            String addrInfo = "";
            if(Objects.nonNull(addr)){
                addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr();
            }
            if(Objects.equals(dbOrder.getDvyType(), DvyType.STATION.value())){
                addrInfo = "自提订单";
            }
            List<OrderItem> orderItems = dbOrder.getOrderItems();
            int firstRow = row + 1;
            int lastRow = row + orderItems.size();
            int col = -1;
            // 订单编号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getOrderNumber());

            // 下单时间
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getCreateTime());
            // 收件人
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getReceiverName());
            // "手机"
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getReceiverMobile());
            // "收货地址"
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, addrInfo);
            int prodNameCol = ++col;
            int prodCountCol = ++col;
            for (OrderItem orderItem : orderItems) {
                row++;
                // 商品名称
                writer.writeCellValue(prodNameCol, row, orderItem.getProdName());
                // 数量
                writer.writeCellValue(prodCountCol, row, orderItem.getProdCount());
            }
            // 订单应付
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getTotal());
            // 订单运费
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getFreightAmount());
            // 订单实付
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getActualTotal());
            //订单状态
            String[] statusValue = {"", "待付款", "待发货", "待收货", "待评价", "成功", "失败", "待成团", ""};
            String status = statusValue[dbOrder.getStatus()];

            String refundStatus = "";
            String[] refundStatusValue = {"","申请退款","退款成功","退款成功","退款失败"};
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, status);
            if (!Objects.isNull(dbOrder.getRefundStatus())){
                refundStatus = refundStatusValue[dbOrder.getRefundStatus()];
            }else {
                refundStatus = "暂无售后";
            }
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, refundStatus);
        }
        PoiExcelUtil.writeExcel(response, writer);
    }

    @Override
    public List<Order> getOrderPayInfoByOrderNumber(List<String> orderNumberList) {
        return orderMapper.getOrderPayInfoByOrderNumber(orderNumberList);
    }

    @Override
    public Order getOrderDetailByOrderNumberAndShopId(String orderNumber, Long shopId, Integer lang) {
        return orderMapper.getOrderDetailByOrderNumberAndShopId(orderNumber,shopId,lang);
    }

    @Override
    public Map<Long, Integer> getOrderItemMap(Long orderId) {
        List<OrderRefund> orderRefunds = orderRefundMapper.selectList(new LambdaQueryWrapper<OrderRefund>()
                .eq(OrderRefund::getOrderId, orderId)
                .lt(OrderRefund::getReturnMoneySts,6)
                .gt(OrderRefund::getReturnMoneySts,0)
        );
        Map<Long, Integer> map = Maps.newHashMap();

        for (OrderRefund orderRefund:orderRefunds){
            if (orderRefund.getRefundType() == 1){
                map.put(0L,orderRefund.getReturnMoneySts());
                return map;
            }
            map.put(orderRefund.getOrderItemId(),orderRefund.getReturnMoneySts());
        }
        return map;
    }

    @Override
    public Order getOrderAndOrderItemByOrderNumber(String orderNumber) {
        return orderMapper.getOrderAndOrderItemByOrderNumber(orderNumber);
    }

    @Override
    public void getOrderItemRefundStatus(Order order) {
        if (Objects.nonNull(order.getRefundStatus()) && order.getRefundStatus() != 4){
            List<OrderRefund> processingOrderRefundByOrderId = orderRefundMapper.getProcessingOrderRefundByOrderId(order.getOrderId());
            for (OrderRefund orderRefund:processingOrderRefundByOrderId){
                //整单退款
                if (orderRefund.getRefundType() == 1){
                    for(OrderItem orderItem:order.getOrderItems()){
                        orderItem.setReturnMoneySts(orderRefund.getReturnMoneySts());
                    }
                    break;
                }
                // 单项退款，每个单号都不一样
                for(OrderItem orderItem:order.getOrderItems()){
                    if (Objects.equals(orderItem.getOrderItemId(), orderRefund.getOrderItemId())) {
                        orderItem.setReturnMoneySts(orderRefund.getReturnMoneySts());
                    }
                }
            }
        }
    }
    @Override
    public List<Order> getOrderAndOrderItemByOrderNumberList(Set<String> orderNumberList) {
        return orderMapper.getOrderAndOrderItemByOrderNumberList(orderNumberList);
    }

    @Override
    public List<CustomerRFMRespTableParam> countPayNumRfm2(CustomerRFMReqParam param) {

        Date recentTime = param.getRecentTime();
        Date mouth = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -30));
        Date threeMouth = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -90));
        Date halfYear = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -180));
        Date year = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -365));
        List<CustomerRFMRespTableParam> dateList = orderMapper.getUserOrderCrateTime(param.getType(),mouth,threeMouth,halfYear,year);
        return dateList;
    }

    @Override
    public UserManagerParam getConsumeData(UserManagerParam param) {
        String userId = param.getUserId();
        // 最近消费时间, 下单支付的时间(积分支付/余额支付), 充值时间， 比较获取最新时间
        Date date = orderMapper.getLastConsumeDateByUserId(param.getUserId());
        param.setReConsTime(date);
        // 消费金额, 累计消费了多少金额：除了积分支付的订单累计金额
        Double amount = orderMapper.countConsumeAmount(userId, 1);
        param.setConsAmount(Objects.nonNull(amount)?amount:0.0);
        // 实付金额, 累计支付的金额: 除了余额支付/积分支付的订单累计金额 + 余额充值金额
        param.setActualAmount(orderMapper.countConsumeAmount(userId, 0));
        // 消费次数, 累计消费的次数（积分下单不包含），下单的次数
        param.setConsTimes(orderMapper.countPayNumByUserId(userId));
        // 平均折扣, 消费下单的优惠总金额/消费次数
        // 优惠总金额
        Double reduceAmount = orderMapper.countReduceAmountByUserId(userId);
        param.setAverDiscount(divAverage(reduceAmount,param.getConsTimes(),2));
        return param;
    }

    @Override
    public IPage<Order> pageByUserId(PageParam<Order> page, String userId) {
        return orderMapper.getPageByUserId(page,userId);
    }

    @Override
    public Integer countByOrderNumber(String orderNumber) {
        return orderMapper.countByOrderNumber(orderNumber);
    }

    @Override
    @Cacheable(cacheNames = "tradeRetained", key = "#param.retainType +':'+ #param.dateType" )
    public List<CustomerRetainRespParam> getTradeRetained(CustomerRetainedReqParam param) {
        return orderMapper.getTradeRetained(param);
    }

    @Override
    @CacheEvict(cacheNames = "tradeRetained", key = "#param.retainType +':'+ #param.dateType")
    public void removeCacheTradeRetained(CustomerRetainedReqParam param) {
    }

    private CustomerRFMRespTableParam countcPayNumRfmParam(CustomerRFMRespTableParam param) {
        int buyers = 0;
        buyers = param.getPayBuyers1()+param.getPayBuyers2()+param.getPayBuyers3()+param.getPayBuyers4()+param.getPayBuyers5();
        param.setPayBuyersTotal(buyers);
        double payTotal = 0.0;
        payTotal = Arith.add(payTotal,param.getPayAmount1());
        payTotal = Arith.add(payTotal,param.getPayAmount2());
        payTotal = Arith.add(payTotal,param.getPayAmount3());
        payTotal = Arith.add(payTotal,param.getPayAmount4());
        payTotal = Arith.add(payTotal,param.getPayAmount5());
        param.setPayAmountTotal(Arith.round(payTotal,2));
        param.setPriceSingle1(divAverage(param.getPayAmount1(),param.getPayBuyers1(),2));
        param.setPriceSingle2(divAverage(param.getPayAmount2(),param.getPayBuyers2(),2));
        param.setPriceSingle3(divAverage(param.getPayAmount3(),param.getPayBuyers3(),2));
        param.setPriceSingle4(divAverage(param.getPayAmount4(),param.getPayBuyers4(),2));
        param.setPriceSingle5(divAverage(param.getPayAmount5(),param.getPayBuyers5(),2));
        double priceSingleTotal = 0.0;
        priceSingleTotal = Arith.add(priceSingleTotal,param.getPriceSingle1());
        priceSingleTotal = Arith.add(priceSingleTotal,param.getPriceSingle2());
        priceSingleTotal = Arith.add(priceSingleTotal,param.getPriceSingle3());
        priceSingleTotal = Arith.add(priceSingleTotal,param.getPriceSingle4());
        priceSingleTotal = Arith.add(priceSingleTotal,param.getPriceSingle5());
        param.setPriceSingleTotal(Arith.round(priceSingleTotal,2));
        return param;
    }

    @Override
    public CustomerRespParam countPayNum(CustomerReqParam param) {
        CustomerRespParam res = new CustomerRespParam();
        param.setDateTime(null);
        res.setPayNum(orderMapper.countPayCustomerNum(param));
        res.setPrePayNum(orderMapper.countPayCustomerNum(param));
        res.setPayNumRate(divAverage(res.getPayNum() - res.getPrePayNum(), res.getPayNum(), 4));
        return res;
    }

    @Override
    public List<CustomerPayParam> countCustomerParam(CustomerReqParam param) {
        List<DateParam> everyDays = DateUtils.findEveryDays(param.getStartTime(), param.getEndTime());
        List<CustomerPayParam> res = new ArrayList<>();
        for (DateParam everyDay : everyDays) {
            CustomerPayParam respParam = new CustomerPayParam();
            param.setStartTime(everyDay.getStartTime());
            param.setEndTime(everyDay.getEndTime());
            param.setDateTime(null);
            respParam.setCurrentDay(DateUtils.dateToStrYmd(everyDay.getStartTime()));
            // 访客数
            respParam.setVisitor(flowPageAnalyseUserMapper.countAllVisitor(null,param.getStartTime(),param.getEndTime()));
            // 累积会员数
            respParam.setMember(userMapper.countMemberByParam(param));
            // 成交客户数
            respParam.setPayNum(orderMapper.countPayCustomerNum(param));
            // 累积粉丝数
            respParam.setFashNum(userMapper.countUserByParam(param));
            res.add(respParam);
        }
        return res;
    }

    @Override
    public Integer countMemberPayNum(CustomerReqParam param) {
        return orderMapper.countMemberPayNum(param);
    }

    @Override
    public CustomerDealRespParam getCustomerDeal(CustomerReqParam param) {
        CustomerDealRespParam res = new CustomerDealRespParam();
        CustomerDealParam allDeal = new CustomerDealParam();
        CustomerDealParam newDeal = new CustomerDealParam();
        CustomerDealParam oldDeal = new CustomerDealParam();
        Date startTime = param.getStartTime();
        Date endTime = param.getEndTime();
        param.setDateTime(null);
        // 客户数
        allDeal.setCustomerNum(orderMapper.countMemberPayNum(param));
        Date beforeYear = DateUtils.getBeforeYear(startTime, -2);
        newDeal.setCustomerNum(orderMapper.countNewMemberPayNum(beforeYear, startTime, endTime));
        oldDeal.setCustomerNum(orderMapper.countOldMemberPayNum(beforeYear, startTime, endTime));
        // 付款金额
        allDeal.setPayAmount(getDouble(orderMapper.countMemberPayAmount(param)));
        newDeal.setPayAmount(getDouble(orderMapper.countNewMemberPayAmount(beforeYear, startTime, endTime)));
        oldDeal.setPayAmount(getDouble(orderMapper.countOldMemberPayAmount(beforeYear, startTime, endTime)));
        // 客户数占比
        param.setStartTime(null);
        param.setEndTime(null);
        Integer allUsers = userMapper.countUserByParam(param);
        allDeal.setCustomerRate(divAverage(allDeal.getCustomerNum(), allUsers, 4));
        newDeal.setCustomerRate(divAverage(newDeal.getCustomerNum(), allDeal.getCustomerNum(), 4));
        oldDeal.setCustomerRate(divAverage(oldDeal.getCustomerNum(), allDeal.getCustomerNum(), 4));
        // 客单价
        allDeal.setCustomerSinglePrice(divAverage(allDeal.getPayAmount(), allDeal.getCustomerNum(), 4));
        newDeal.setCustomerSinglePrice(divAverage(newDeal.getPayAmount(), newDeal.getCustomerNum(), 4));
        oldDeal.setCustomerSinglePrice(divAverage(oldDeal.getPayAmount(), oldDeal.getCustomerNum(), 4));
        // 访问-付款转化率
        // 两年的总访客数
        Integer totalVisitor = flowPageAnalyseUserMapper.countVisitorNumByDate(beforeYear,endTime);
        // 支付访客数
        Integer payVisitor = flowPageAnalyseUserMapper.countPaidVisitorNumByDate(beforeYear,endTime);
        // 未支付访客数 = 两年的总访客数 - 支付访客数
        allDeal.setTransRate(divAverage(allDeal.getCustomerNum(),totalVisitor,4));
        newDeal.setTransRate(divAverage(newDeal.getCustomerNum(),totalVisitor-payVisitor,4));
        oldDeal.setTransRate(divAverage(oldDeal.getCustomerNum(),payVisitor,4));
        res.setAllDeal(allDeal);
        res.setNewDeal(newDeal);
        res.setOldDeal(oldDeal);
        List<CustomerDealTrendParam> listDeal = new ArrayList<>();
        List<DateParam> everyDays = DateUtils.findEveryDays(startTime, endTime);
        for (DateParam everyDay : everyDays) {
            CustomerDealTrendParam trend = new CustomerDealTrendParam();
            Date startTime1 = everyDay.getStartTime();
            Date endTime1 = everyDay.getEndTime();
            trend.setCurrentDay(DateUtils.dateToStrYmd(everyDay.getEndTime()));
            trend.setNewCustomerNum(orderMapper.countNewMemberPayNum(beforeYear, startTime1, endTime1));
            trend.setOldCustomerNum(orderMapper.countOldMemberPayNum(beforeYear, startTime1, endTime1));
            trend.setNewPayAmount(getDouble(orderMapper.countNewMemberPayAmount(beforeYear, startTime1, endTime1)));
            trend.setOldPayAmount(getDouble(orderMapper.countOldMemberPayAmount(beforeYear, startTime1, endTime1)));
            trend.setNewCustomerSinglePrice(divAverage(trend.getNewPayAmount(), trend.getNewCustomerNum(), 4));
            trend.setOldCustomerSinglePrice(divAverage(trend.getOldPayAmount(), trend.getOldCustomerNum(), 4));
            trend.setNewTransRate(divAverage(trend.getNewCustomerNum(),totalVisitor-payVisitor,4));
            trend.setOldTransRate(divAverage(trend.getOldCustomerNum(),payVisitor,4));
            listDeal.add(trend);
        }
        res.setListDeal(listDeal);
        return res;
    }

    @Override
    public List<CustomerRFMRespParam> countPayNumRfm(CustomerRFMReqParam param) {
        List<CustomerRFMRespParam> list = new ArrayList<>();
        Integer type = param.getType();
        Date recentTime = param.getRecentTime();
        // 1 2 3 4 f=5 表示f>=5
        Integer f = 1;
        // R<=30
        Date startTime = new Date();
        Date endTime = recentTime;
        for (int i = 1; i < 6; i++) {
            CustomerRFMRespParam param1 = new CustomerRFMRespParam();
            param1.setRecency(1);
            param1.setFrequency(i);
            startTime = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -30));
            endTime = recentTime;
            param1.setPayBuyers(orderMapper.countPayNumRfm(1, i, startTime, endTime));
            param1.setPayAmount(getDouble(orderMapper.sumPayAmountRfm(1, i, startTime, endTime)));
            list.add(param1);
        }
        // 30<R<=90
        for (int i = 1; i < 6; i++) {
            CustomerRFMRespParam param2 = new CustomerRFMRespParam();
            param2.setRecency(2);
            param2.setFrequency(i);
            startTime = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -90));
            endTime = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -30));
            param2.setPayBuyers(orderMapper.countPayNumRfm(1, i, startTime, endTime));
            param2.setPayAmount(getDouble(orderMapper.sumPayAmountRfm(1, i, startTime, endTime)));
            list.add(param2);
        }
        // 90<R<=180
        for (int i = 1; i < 6; i++) {
            CustomerRFMRespParam param3 = new CustomerRFMRespParam();
            param3.setRecency(3);
            param3.setFrequency(i);
            startTime = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -180));
            endTime = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -90));
            param3.setPayBuyers(orderMapper.countPayNumRfm(1, i, startTime, endTime));
            param3.setPayAmount(getDouble(orderMapper.sumPayAmountRfm(1, i, startTime, endTime)));
            list.add(param3);
        }
        // 180<R<=365
        for (int i = 1; i < 6; i++) {
            CustomerRFMRespParam param4 = new CustomerRFMRespParam();
            param4.setRecency(4);
            param4.setFrequency(i);
            startTime = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -365));
            endTime = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -180));
            param4.setPayBuyers(orderMapper.countPayNumRfm(1, i, startTime, endTime));
            param4.setPayAmount(getDouble(orderMapper.sumPayAmountRfm(1, i, startTime, endTime)));
            list.add(param4);
        }
        // R > 365
        for (int i = 1; i < 6; i++) {
            CustomerRFMRespParam param4 = new CustomerRFMRespParam();
            param4.setRecency(5);
            param4.setFrequency(i);
            Integer payBuyers = 0;
            Double amount = 0.0;
            if (Objects.equals(0, type)) {
                startTime = DateUtils.dateToFast(DateUtils.getBeforeDay(recentTime, -365));
                payBuyers = orderMapper.countPayNumRfm(0, i, startTime, null);
                amount = getDouble(orderMapper.sumPayAmountRfm(0, i, startTime, null));
            }
            param4.setPayBuyers(payBuyers);
            param4.setPayAmount(amount);
            list.add(param4);
        }
        return list;
    }

    @Override
    public CustomerConsumeRespParam getConsumePower(CustomerConsumeReqParam param) {
        CustomerConsumeRespParam respParam = new CustomerConsumeRespParam();
        respParam.setStart(0.0D);
        Date startTime = param.getStartTime();
        Date endTime = param.getEndTime();
        Date beforeYear = DateUtils.getBeforeYear(startTime, -2);
        Integer type = param.getType();
        // 0全部成交客户 1新成交客户 2老成交客户
        if (!Objects.equals(2, type)) {
            respParam.setNewPayBuyers(orderMapper.countNewMemberPayNum(beforeYear, startTime, endTime));
            respParam.setNewPayAmount(getDouble(orderMapper.countNewMemberPayAmount(beforeYear, startTime, endTime)));
            respParam.setNewPayProdCount(getInteger(orderMapper.countNewOrOldPayProdNum(0, beforeYear, startTime, endTime)));
        }
        if (Objects.equals(1, type)) {
            respParam.setEnd(Math.ceil(respParam.getNewPayAmount()));
            return respParam;
        }
        respParam.setOldPayBuyers(orderMapper.countOldMemberPayNum(beforeYear, startTime, endTime));
        respParam.setOldPayAmount(getDouble(orderMapper.countOldMemberPayAmount(beforeYear, startTime, endTime)));
        respParam.setOldPayProdCount(getInteger(orderMapper.countNewOrOldPayProdNum(1, beforeYear, startTime, endTime)));
        if (Objects.equals(2, type)) {
            respParam.setEnd(Math.ceil(respParam.getOldPayAmount()));
            return respParam;
        }
        respParam.setEnd(Math.ceil(Arith.add(respParam.getNewPayAmount(), respParam.getOldPayAmount())));
        if (respParam.getEnd() <= 50) {
            respParam.setEnd(50.0);
        }
        return respParam;
    }

    @Override
    public CustomerConsumeRespParam getConsumeFrequency(CustomerConsumeReqParam param) {
        CustomerConsumeRespParam respParam = new CustomerConsumeRespParam();
        Integer type = param.getType();
        // 0全部成交客户 1新成交客户 2老成交客户
        Date startTime = param.getStartTime();
        Date endTime = param.getEndTime();
        Date beforeYear = DateUtils.getBeforeYear(startTime, -2);
        if (!Objects.equals(2, type)) {
            respParam.setNewPayAmount(getDouble(orderMapper.countNewMemberPayAmount(beforeYear, startTime, endTime)));
            respParam.setNewPayBuyers(orderMapper.countNewMemberPayNum(beforeYear, startTime, endTime));
            respParam.setNewPayProdCount(getInteger(orderMapper.countNewOrOldPayProdNum(0, beforeYear, startTime, endTime)));
        }
        Integer newPayTimes = orderMapper.countNewOrOldPayTimes(0, beforeYear, startTime, endTime);
        if (Objects.equals(1, type)) {
            respParam.setEnd(Double.valueOf(newPayTimes));
            return respParam;
        }
        respParam.setOldPayAmount(getDouble(orderMapper.countOldMemberPayAmount(beforeYear, startTime, endTime)));
        respParam.setOldPayBuyers(orderMapper.countOldMemberPayNum(beforeYear, startTime, endTime));
        respParam.setOldPayProdCount(getInteger(orderMapper.countNewOrOldPayProdNum(1, beforeYear, startTime, endTime)));
        Integer oldPayTimes = orderMapper.countNewOrOldPayTimes(1, beforeYear, startTime, endTime);
        if (Objects.equals(2, type)) {
            respParam.setEnd(Double.valueOf(oldPayTimes));
            return respParam;
        }
        respParam.setEnd(Double.valueOf(newPayTimes + oldPayTimes));
        return respParam;
    }

    @Override
    public CustomerRepurchaseRespParam getConsumeRepurchaseCount(CustomerConsumeReqParam param) {
        Integer type = param.getType();
        // 0全部成交客户 1新成交客户 2老成交客户
        Date endTime = param.getEndTime();
        Date startTime = param.getStartTime();
        Date beforeYear = DateUtils.getBeforeYear(startTime, -2);
        Integer cycle = -param.getCycle();
        Integer payBuyers = 0;
        Double payAmount = 0.0;
        Integer payProdCount = 0;
        Integer repurchaseCount = 0;
        CustomerRepurchaseRespParam respParam = new CustomerRepurchaseRespParam();
        List<CustomerRepurchaseDetailParam> res = new ArrayList<>();
        CustomerRepurchaseDetailParam deParam = new CustomerRepurchaseDetailParam();
        if (!Objects.equals(2, type)) {
            // 全部/新客户
            List<CustomerOrderParam> orderParams = orderMapper.getNewRepurchaseParam(param.getShopId(), 0, beforeYear, startTime, endTime);
            ArrayList<CustomerOrderParam> collect = orderParams.stream().collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparing(CustomerOrderParam::getUserId))), ArrayList::new));
            payBuyers = payBuyers + collect.size();
            for (CustomerOrderParam orderParam : collect) {
                CustomerRepurchaseDetailParam detailParam = orderMapper.getNewRepurchaseCount(param.getShopId(), 0, cycle, orderParam.getUserId(), orderParam.getPayTime(), beforeYear, startTime, endTime);
                payAmount = Arith.add(payAmount, getDouble(detailParam.getPayAmount()));
                payProdCount = payProdCount + detailParam.getPayProdCount();
                repurchaseCount = repurchaseCount + detailParam.getRepurchaseCount();
            }
        }
        if (!Objects.equals(1, type)) {
            // 全部/老客户
            List<CustomerOrderParam> orderParams = orderMapper.getNewRepurchaseParam(param.getShopId(), 1, beforeYear, startTime, endTime);
            ArrayList<CustomerOrderParam> collect = orderParams.stream().collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparing(CustomerOrderParam::getUserId))), ArrayList::new));
            payBuyers = payBuyers + collect.size();
            for (CustomerOrderParam orderParam : collect) {
                CustomerRepurchaseDetailParam detailParam = orderMapper.getNewRepurchaseCount(param.getShopId(), 1, cycle, orderParam.getUserId(), orderParam.getPayTime(), beforeYear, startTime, endTime);
                payAmount = Arith.add(payAmount, getDouble(detailParam.getPayAmount()));
                payProdCount = payProdCount + detailParam.getPayProdCount();
                repurchaseCount = repurchaseCount + detailParam.getRepurchaseCount();
            }
        }
        deParam.setPayAmount(payAmount);
        deParam.setPayBuyers(payBuyers);
        deParam.setPayProdCount(payProdCount);
        deParam.setRepurchaseCount(repurchaseCount);
        res.add(deParam);
        respParam.setDetail(res);
        return respParam;
    }

    @Override
    public MemberContributeRespParam getMemberContributeValue(MemberReqParam param) {
        Date startTime = param.getStartTime();
        Date endTime = param.getEndTime();
        MemberContributeRespParam respParam = new MemberContributeRespParam();
        // 普通会员
        MemberContributeValueParam publicMember = new MemberContributeValueParam();
        // 付费会员
        MemberContributeValueParam paidMember = new MemberContributeValueParam();

        // 普通会员
        param.setDateTime(endTime);
        param.setMemberType(1);
        publicMember.setTotalMember(userExtensionMapper.countMemberByParam(param));
        param.setDateTime(null);
        publicMember.setPayMemberNum(orderMapper.countPaidMemberByParam(param,null));
        // TODO 支付频次暂不统计 时间段内：支付订单数 / 消费人数
        publicMember.setPayOrderNum(orderMapper.countPaidMemberByParam(param,1));
        publicMember.setFrequencyOfConsume(divAverage(publicMember.getPayOrderNum(),publicMember.getPayMemberNum(),2));
        publicMember.setPayAmount(getDouble(orderMapper.countMemberPaidAmount(param)));
        publicMember.setPricePerMember(divAverage(publicMember.getPayAmount(),publicMember.getPayMemberNum(),4));
        // 付费会员
        param.setMemberType(2);
        param.setDateTime(endTime);
        paidMember.setTotalMember(userExtensionMapper.countMemberByParam(param));
        param.setDateTime(null);
        paidMember.setPayMemberNum(orderMapper.countPaidMemberByParam(param,null));
        paidMember.setPayAmount(getDouble(orderMapper.countMemberPaidAmount(param)));
        paidMember.setPayOrderNum(orderMapper.countPaidMemberByParam(param,1));
        paidMember.setFrequencyOfConsume(divAverage(paidMember.getPayOrderNum(),paidMember.getPayMemberNum(),2));
        paidMember.setPricePerMember(divAverage(paidMember.getPayAmount(),paidMember.getPayMemberNum(),4));
        publicMember.setTotalMemberRate(divAverage(publicMember.getTotalMember(),publicMember.getTotalMember()+paidMember.getTotalMember(),4));
        paidMember.setTotalMemberRate(Arith.sub(1,publicMember.getTotalMemberRate()));
        publicMember.setPayMemberNumRate(divAverage(publicMember.getPayMemberNum(),publicMember.getPayMemberNum()+ paidMember.getPayMemberNum(),4));
        paidMember.setPayMemberNumRate(Arith.sub(1,publicMember.getPayMemberNumRate()));
        publicMember.setPayAmountRate(divAverage(publicMember.getPayAmount(),Arith.add(publicMember.getPayAmount(),paidMember.getPayAmount()),4));
        paidMember.setPayAmountRate(Arith.sub(1,publicMember.getPayAmountRate()));

        respParam.setPublicMember(publicMember);
        respParam.setPaidMember(paidMember);
        return respParam;
    }

    @Override
    public MemberDealRespParam getMemberDeal(MemberReqParam param) {
        MemberDealRespParam respParam = new MemberDealRespParam();
        MemberDealParam allMember = new MemberDealParam();
        MemberDealParam newMember = new MemberDealParam();
        MemberDealParam oldMember = new MemberDealParam();
        List<MemberDealTreadParam> trend = new ArrayList<>();
        Date startTime = param.getStartTime();
        Date endTime = param.getEndTime();
        param.setDateTime(null);
        // 客户数
        allMember.setPayMemberNum(orderMapper.countMemberPayNum(param));
        Date beforeYear = DateUtils.getBeforeYear(startTime, -2);
        newMember.setPayMemberNum(orderMapper.countNewMemberPayNum(beforeYear, startTime, endTime));
        oldMember.setPayMemberNum(orderMapper.countOldMemberPayNum(beforeYear, startTime, endTime));
        // 成交会员占比
        Integer payMemberNum = allMember.getPayMemberNum();
        allMember.setPayMemberNumRate(1.0);
        newMember.setPayMemberNumRate(divAverage(newMember.getPayMemberNum(),payMemberNum,4));
        oldMember.setPayMemberNumRate(divAverage(oldMember.getPayMemberNum(),payMemberNum,4));
        // 付款金额
        allMember.setPayAmount(getDouble(orderMapper.countMemberPayAmount(param)));
        newMember.setPayAmount(getDouble(orderMapper.countNewMemberPayAmount(beforeYear, startTime, endTime)));
        oldMember.setPayAmount(getDouble(orderMapper.countOldMemberPayAmount(beforeYear, startTime, endTime)));
        // 支付金额占比
        allMember.setPayAmountRate(1.0);
        newMember.setPayAmountRate(divAverage(newMember.getPayAmount(),allMember.getPayAmount(),4));
        oldMember.setPayAmountRate(Arith.sub(allMember.getPayAmountRate(),newMember.getPayAmountRate()));
        // 客单价
        allMember.setPricePerMember(divAverage(allMember.getPayAmount(),allMember.getPayMemberNum(),2));
        newMember.setPricePerMember(divAverage(newMember.getPayAmount(),newMember.getPayMemberNum(),2));
        oldMember.setPricePerMember(divAverage(oldMember.getPayAmount(),oldMember.getPayMemberNum(),2));
        // 支付订单数
        newMember.setPayOrderNum(orderMapper.countNewOrOldMemberPayOrder(0,beforeYear, startTime, endTime));
        oldMember.setPayOrderNum(orderMapper.countNewOrOldMemberPayOrder(1,beforeYear, startTime, endTime));
        allMember.setPayOrderNum(newMember.getPayOrderNum() + oldMember.getPayOrderNum());
        respParam.setAllMember(allMember);
        respParam.setNewMember(newMember);
        respParam.setOldMember(oldMember);
        List<DateParam> everyDays = DateUtils.findEveryDays(startTime, endTime);
        for (DateParam everyDay : everyDays) {
            MemberDealTreadParam res = new MemberDealTreadParam();
            Date dayStartTime = everyDay.getStartTime();
            Date dayEndTime = everyDay.getEndTime();
            res.setCurrentDay(DateUtils.dateToNumber(dayStartTime));
            res.setNewPayMemberNum(orderMapper.countNewMemberEveryDayPayNum(0,beforeYear, startTime,dayStartTime, dayEndTime));
            res.setOldPayMemberNum(orderMapper.countNewMemberEveryDayPayNum(1,beforeYear, startTime,dayStartTime, dayEndTime));
            res.setNewPayOrderNum(orderMapper.countNewOrOldMemberEveryDayPayPayOrder(0,beforeYear, startTime,dayStartTime, dayEndTime));
            res.setOldPayOrderNum(orderMapper.countNewOrOldMemberEveryDayPayPayOrder(1,beforeYear, startTime,dayStartTime, dayEndTime));
            res.setNewPayAmount(getDouble(orderMapper.countNewMemberEveryDayPayAmount(0,beforeYear, startTime,dayStartTime, dayEndTime)));
            res.setOldPayAmount(getDouble(orderMapper.countNewMemberEveryDayPayAmount(1,beforeYear, startTime,dayStartTime, dayEndTime)));
            res.setNewPricePerMember(divAverage(res.getNewPayAmount(),res.getNewPayMemberNum(),2));
            res.setOldpricePerMember(divAverage(res.getOldPayAmount(),res.getOldPayMemberNum(),2));
            trend.add(res);
        }
        respParam.setTrendParam(trend);
        return respParam;
    }

    private Double getDouble(Double value) {
        if (Objects.isNull(value)) {
            return 0.0;
        }
        return value;
    }

    private Integer getInteger(Integer value) {
        if (Objects.isNull(value)) {
            return 0;
        }
        return value;
    }

    private Double divAverage(Integer a, Integer b, Integer scale) {
        if (Objects.isNull(b) || b == 0 || Objects.isNull(a)) {
            return 0.0;
        }
        return Arith.div(a, b, scale);
    }

    private Double divAverage(Double a, Integer b, Integer scale) {
        if (Objects.isNull(b) || b == 0 || Objects.isNull(a)) {
            return 0.0;
        }
        return Arith.div(a, b, scale);
    }
    private Double divAverage(Double a, Double b, Integer scale) {
        if (Objects.isNull(b) || b == 0 || Objects.isNull(a)) {
            return 0.0;
        }
        return Arith.div(a, b, scale);
    }


    @Override
    public IPage<MyOrderDto> orderListByStatus(PageParam<MyOrderDto> page, Integer status, Long stationId) {
        page.setRecords(orderMapper.orderListByStatusAndStationId(new PageAdapter(page), status, stationId));
        List<MyOrderDto> orderDtoList = page.getRecords();
        for (MyOrderDto myOrderDto : orderDtoList) {
            if(StrUtil.isBlank(myOrderDto.getShopName())){
                myOrderDto.setShopName(Constant.PLATFORM_SHOP_NAME);
            }
        }
        page.setTotal(orderMapper.countMyOrderByUserIdAndStatus(null, status,null,stationId));
        return page;
    }

    @Override
    public Boolean orderStationByOrderNumber(List<String> orderNumberList, String userId, Long stationId) {
        if(CollUtil.isEmpty(orderNumberList)){
            // 请选择最少一个需要自提的订单
            throw new YamiShopBindException("yami.order.need.station");
        }
        List<Order> orderList = orderMapper.getStationOrderByOrderNumbers(orderNumberList,userId,stationId);
        Order orderParam = new Order();
        if(CollectionUtils.isEmpty(orderList)){
            // 订单不存在
            throw new YamiShopBindException("yami.order.no.exist");
        }
        for (Order order:orderList){
            if(Objects.isNull(order)){
                // 订单不存在
                throw new YamiShopBindException("yami.order.no.exist");
            }
            boolean isRefund = Objects.equals(order.getRefundStatus(), RefundStatusEnum.APPLY.value())
                    || Objects.equals(order.getRefundStatus(), RefundStatusEnum.SUCCEED.value());
            if (Objects.nonNull(order.getRefundStatus()) && isRefund) {
                // 订单退款中，无法自提
                throw new YamiShopBindException(I18nMessage.getMessage("yami.order")+ order.getOrderNumber() + I18nMessage.getMessage("yami.order.station.check"));
            }
            if (!Objects.equals(order.getStatus(),OrderStatus.PADYED.value())){
                // 订单已提货
                throw new YamiShopBindException(I18nMessage.getMessage("yami.order") + order.getOrderNumber() + I18nMessage.getMessage("yami.order.station.finish"));
            }
//            if(Objects.isNull(orderParam)){
            orderParam = order;
//            }
            order.setStatus(OrderStatus.CONSIGNMENT.value());
            order.setDvyTime(new Date());

        }
        updateBatchById(orderList);

        // 消息推送-自提点核销通知
        if (orderList.size() > 1){
            orderParam.setOrderNumber(orderParam.getOrderNumber() + "......");
        }
        if(Objects.isNull(stationId)){
            stationId = orderParam.getDvyId();
        }
        Station station = stationService.getById(stationId);
        notifyTemplateService.writeOffOrder(station.getStationName(),orderParam);
        return true;
    }

    private void writeExcel(HttpServletResponse response, ExcelWriter writer) {
        //response为HttpServletResponse对象
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        //test.xls是弹出下载对话框的文件名，不能为中文，中文请自行编码
        response.setHeader("Content-Disposition", "attachment;filename=1.xls");

        ServletOutputStream servletOutputStream = null;
        try {
            servletOutputStream = response.getOutputStream();
            writer.flush(servletOutputStream);
            servletOutputStream.flush();
        } catch (IORuntimeException | IOException e) {
            e.printStackTrace();
        } finally {
            writer.close();
            try {
                if (servletOutputStream != null) {
                    servletOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
